-
Notifications
You must be signed in to change notification settings - Fork 578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discard likely duplicate problem notifications via Notification#last_notified_state_per_user #9893
Discard likely duplicate problem notifications via Notification#last_notified_state_per_user #9893
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from the comments below, please also document the new cluster events here.
40244e6
to
4b51ec9
Compare
test/icinga-notification.cpp
Outdated
Application::GetTP().Start(); | ||
helper.n->BeginExecuteNotification(NotificationRecovery, nullptr, false, false, "", ""); | ||
Application::GetTP().Stop(); | ||
BOOST_CHECK(helper.called == 1u); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might also be worth adding this test here:
BOOST_CHECK(helper.n->GetLastNotifiedStatePerUser()->Get(helper.u->GetName()) == Empty);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant IMAO. It's only the bridge to (not) sending a notification, the latter is important and tested here.
4b51ec9
to
5adefce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please also deduplicate the remaining test cases.
lib/icinga/clusterevents.cpp
Outdated
notification->GetLastNotifiedStatePerUser()->Set(params->Get("user"), state); | ||
Notification::OnLastNotifiedStateOfUserUpdated(notification, params->Get("user"), state, origin); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The attributes and signals are named inconsistently ("per" vs. "of"). For what it's worth, my preference would be unifying towards "per".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guilty. To my defence: The whole thing contains all states, separately per user. But sometimes I want to update only the value of a particular user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But wouldn't it make so much more sense if everything that's specifically related to syncing that attribute was also named like that attribute and not just somewhat similar? Actually, this affects OnLastNotifiedStatesCleared
as well.
1055f29
to
d72906b
Compare
d72906b
to
c1fe30b
Compare
c1fe30b
to
77a2494
Compare
77a2494
to
b3b86a6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning
# curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
-X POST 'https://localhost:5666/v1/actions/process-check-result' \
-d '{ "type": "Service", "filter": "service.name==\"test\"", "exit_status": 1, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "pretty": true }'
[2023-12-12 16:17:21 +0100] notice/Notification: Attempting to send notifications of type 'Problem' for notification object 'icinga2!test!notify'.
[2023-12-12 16:17:21 +0100] notice/Notification: Not sending notifications for notification object 'icinga2!test!notify and user 'yhabteab': state 'Warning' does not match state filter: Critical, OK and Unknown.
Critical:
# curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
-X POST 'https://localhost:5666/v1/actions/process-check-result' \
-d '{ "type": "Service", "filter": "service.name==\"test\"", "exit_status": 2, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "pretty": true }'
[2023-12-12 16:18:35 +0100] notice/Notification: Attempting to send notifications of type 'Problem' for notification object 'icinga2!test!notify'.
[2023-12-12 16:18:35 +0100] information/Notification: Sending 'Problem' notification 'icinga2!test!notify' for user 'yhabteab'
[2023-12-12 16:18:35 +0100] information/Notification: Completed sending 'Problem' notification 'icinga2!test!notify' for checkable 'icinga2!test' and user 'yhabteab' using command 'noop'.
Critical -> Warning -> Critical:
# curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
-X POST 'https://localhost:5666/v1/actions/process-check-result' \
-d '{ "type": "Service", "filter": "service.name==\"test\"", "exit_status": 1, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "pretty": true }'
# curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
-X POST 'https://localhost:5666/v1/actions/process-check-result' \
-d '{ "type": "Service", "filter": "service.name==\"test\"", "exit_status": 2, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "pretty": true }'
[2023-12-12 16:21:48 +0100] notice/Notification: Attempting to send notifications of type 'Problem' for notification object 'icinga2!test!notify'.
[2023-12-12 16:21:48 +0100] notice/Notification: Notification object 'icinga2!test!notify': We already notified user 'yhabteab' for a Critical problem. Likely after that another state change notification was filtered out by config. Not sending duplicate 'Critical' notification.
Recovery -> Critical:
# curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
-X POST 'https://localhost:5666/v1/actions/process-check-result' \
-d '{ "type": "Service", "filter": "service.name==\"test\"", "exit_status": 0, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "pretty": true }'
[2023-12-12 16:24:00 +0100] information/Notification: Completed sending 'Recovery' notification 'icinga2!test!notify' for checkable 'icinga2!test' and user 'yhabteab' using command 'noop'.
# curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
-X POST 'https://localhost:5666/v1/actions/process-check-result' \
-d '{ "type": "Service", "filter": "service.name==\"test\"", "exit_status": 2, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "pretty": true }'
[2023-12-12 16:24:20 +0100] information/Notification: Completed sending 'Problem' notification 'icinga2!test!notify' for checkable 'icinga2!test' and user 'yhabteab' using command 'noop'.
test/icinga-notification.cpp
Outdated
n->SetUsersRaw(new Array({"jdoe"}), true); | ||
n->SetTypeFilter(typeFilter); | ||
n->SetStateFilter(stateFilter); | ||
((ConfigObject*)n.get())->OnAllConfigLoaded(); // link Service |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of this cast? ConfigObject::OnAllConfigLoaded()
is virtual, so shouldn't this just call n->OnAllConfigLoaded()
anyways?
And if a cast is actually necessary, we have static_pointer_cast
/dynamic_pointer_cast
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config::OnAllConfigLoaded()
is virtual, so shouldn't this just calln->OnAllConfigLoaded()
anyways?
I suppose it's because Notification::OnAllConfigLoaded()
isn't publicly accessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if a cast is actually necessary, we have
static_pointer_cast
/dynamic_pointer_cast
.
I don't get the point of these. I'm casting to base, not to derived.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any good reason why Notification::OnAllConfigLoaded()
shouldn't be public if it implements a public virtual method of a base class? So unless there's a good reason, I'd just change the visibility and avoid strange casts altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely! IMAO always the overriding method of the most derived class shall be called from external, none of the base methods. Protecting all ex. the base virtual one enforces this. OK, admittedly Notification is the most derived class. But if this changes for Notification (or any other class) in the future, one had to remember these rules and make all methods protected the new derived class overrides. To make some already public methods protected!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either I'm getting you wrong or what you're describing can't happen.
https://en.cppreference.com/w/cpp/language/virtual#In_detail:
If some member function vf is declared as virtual in a class Base, and some class Derived, which is derived, directly or indirectly, from Base, has a declaration for member function with the same [...]
Then this function in the class Derived is also virtual (whether or not the keyword virtual is used in its declaration) and overrides Base::vf (whether or not the word override is used in its declaration).
So Notification::OnAllConfigLoaded()
is virtual automatically and if there was another derived class overriding it, it would still be used.
lib/icinga/notification.hpp
Outdated
@@ -92,6 +93,8 @@ class Notification final : public ObjectImpl<Notification> | |||
static String NotificationHostStateToString(HostState state); | |||
|
|||
static boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> OnNextNotificationChanged; | |||
static boost::signals2::signal<void (const Notification::Ptr&, const String&, uint_fast8_t, const MessageOrigin::Ptr&)> OnLastNotifiedStatePerUserUpdated; | |||
static boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> OnLastNotifiedStatesCleared; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also rename this to OnLastNotifiedStatePerUserCleared
. Why? Because a simple git grep LastNotifiedStatePerUser
will then lead you to all the places that affect this attribute.
b3b86a6
to
ef153bc
Compare
…blem notifications
…ied_state_per_user
ef153bc
to
97cd05d
Compare
fixes #4503
Commits
Test
Tested with https://github.com/Al2Klimov/twintowers and following config in
volumes/icinga2/master1/etc/icinga2/zones.d/master/my.conf